import { BTErr } from "../err/BTErr";
import { Cls } from "../util/Defined";
import { BTNode } from "./BTNode";
import { BTState } from "./BTState";
import { BTAction } from "./action/BTAction";
import { BTWait } from "./action/BTWait";
import { BTComposite } from "./composite/BTComposite";
import { BTParallel } from "./composite/BTParallel";
import { BTParallelSelector } from "./composite/BTParallelSelector";
import { BTSelector } from "./composite/BTSelector";
import { BTSecquence } from "./composite/BTSequence";
import { BTAlwaysFail } from "./decorator/BTAlwaysFail";
import { BTAlwaysSucc } from "./decorator/BTAlwaysSucc";
import { BTConditional as BTConditional } from "./decorator/BTConditional";
import { BTInverter } from "./decorator/BTInverter";
import { BTRepeater } from "./decorator/BTRepeater";
import { BTUntilFail } from "./decorator/BTUntilFail";
import { BTUntilSucc } from "./decorator/BTUntilSucc";
import { BTCondition } from "./action/BTCondition";
import { BTAbortType } from "./composite/BTAbortType";

/**
 * [行为树]构建
 */
export class BTBuilder<T = any> {

    protected _node?: BTNode;

    build() {
        const node = this._node;
        this._node = null;
        return node;
    }

    protected addNode(node: BTNode) {
        if (this._node) {
            throw new BTErr("构建失败!非复合构建只能有一个节点。");
        }
        this._node = node;
        return this;
    }

    // ============ ACTION ============ 

    action(action: (ctx: T) => BTState) {
        return this.addNode(new BTAction(action));
    }

    condition(action: (ctx: T) => BTState) {
        return this.addNode(new BTCondition(action));
    }

    conditionB(action: (ctx: T) => boolean) {
        return this.condition(ctx => action(ctx) ? BTState.succ : BTState.fail);
    }

    wait(waitTime: number | ((ctx: T) => number), getTime?: (ctx: T) => number) {
        return this.addNode(new BTWait(waitTime, getTime));
    }

    // ============ DECORATOR ============ 

    protected buildChild(buildActions: (builder: BTBuilder<T>) => void) {
        const builder = new BTBuilder();
        buildActions(builder);
        return builder.build();
    }

    conditional(action: (ctx: T) => BTState, buildAction: (builder: BTBuilder<T>) => void, shouldReevaluate: boolean = true) {
        const conditional = new BTCondition<T>(action);
        return this.addNode(new BTConditional(conditional, this.buildChild(buildAction), shouldReevaluate));
    }

    conditionalB(action: (ctx: T) => boolean, buildAction: (builder: BTBuilder<T>) => void, shouldReevaluate: boolean = true) {
        return this.conditional(ctx => action(ctx) ? BTState.succ : BTState.fail, buildAction, shouldReevaluate);
    }

    repeater(repeat: number, buildAction: (builder: BTBuilder<T>) => void, endOnFail?: boolean) {
        return this.addNode(new BTRepeater(repeat, endOnFail, this.buildChild(buildAction)));
    }

    inverter(buildAction: (builder: BTBuilder<T>) => void) {
        return this.addNode(new BTInverter(this.buildChild(buildAction)));
    }

    alwaysSucc(buildAction: (builder: BTBuilder<T>) => void) {
        return this.addNode(new BTAlwaysSucc(this.buildChild(buildAction)));
    }

    alwaysFail(buildAction: (builder: BTBuilder<T>) => void) {
        return this.addNode(new BTAlwaysFail(this.buildChild(buildAction)));
    }

    untilSucc(buildAction: (builder: BTBuilder<T>) => void) {
        return this.addNode(new BTUntilSucc(this.buildChild(buildAction)));
    }

    untilFail(buildAction: (builder: BTBuilder<T>) => void) {
        return this.addNode(new BTUntilFail(this.buildChild(buildAction)));
    }

    // ============ COMPOSITE ============ 

    protected buildComposite(composite: Cls<BTComposite>, buildActions: (builder: BTCompositeBuilder<T>) => void, abortType = BTAbortType.none) {
        const builder = new BTCompositeBuilder(new composite([], abortType));
        buildActions(builder);
        return this.addNode(builder.build());
    }

    sequence(buildActions: (builder: BTCompositeBuilder<T>) => void, abortType = BTAbortType.none) {
        return this.buildComposite(BTSecquence, buildActions, abortType);
    }

    selector(buildActions: (builder: BTCompositeBuilder<T>) => void, abortType = BTAbortType.none) {
        return this.buildComposite(BTSelector, buildActions, abortType);
    }

    parallel(buildActions: (builder: BTCompositeBuilder<T>) => void) {
        return this.buildComposite(BTParallel, buildActions);
    }

    parallelSelector(buildActions: (builder: BTCompositeBuilder<T>) => void) {
        return this.buildComposite(BTParallelSelector, buildActions);
    }

}

/**
 * [行为树]复合构建
 */
export class BTCompositeBuilder<T = any> extends BTBuilder<T> {

    protected _node: BTComposite<T>;

    constructor(composite: BTComposite<T>) {
        super();
        this._node = composite;
    }

    protected addNode(node: BTNode<T>): this {
        this._node.addChild(node);
        return this;
    }

}